/*
 * Copyright 2009-2010 Nanjing RedOrange ltd (http://www.red-orange.cn)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package redora.client.util;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.*;
import redora.client.Fields;
import redora.client.constants.RedoraConstants;
import redora.client.ui.DateBox;
import redora.client.ui.EnumWidget;
import redora.client.ui.Label;
import redora.client.ui.RedoraResource;
import redora.client.ui.text.RichTextArea;

import java.util.Date;
import java.util.Iterator;
import java.util.logging.Logger;

import static com.google.gwt.user.client.ui.FormPanel.ENCODING_URLENCODED;
import static com.google.gwt.user.client.ui.FormPanel.METHOD_POST;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.SEVERE;
import static redora.client.util.ClientUtil.DATE;
import static redora.client.util.ClientUtil.DATETIME;
import static redora.client.util.GWTViewUtil.InputStyle.table;

/**
 * Common methods for create widgets used in GWT's data table.
 *
 * @author Nanjing RedOrange (www.red-orange.cn)
 */
public class GWTViewUtil {
    static Logger l = Logger.getLogger("GWTViewUtil");

    static RedoraResource resources = GWT.create(RedoraResource.class);
    static RedoraConstants constants = GWT.create(RedoraConstants.class);

    public static final String NULL = "null";

    public enum SelectionPolicy {

        CheckBox, Radio, None
    }

    public enum InputStyle {
        form, table
    }

    public static Widget getInputWidget(Field field, InputStyle inputStyle) {
        return getInputWidget(field, null, true, inputStyle);
    }

    /**
     * Creates an appropriate Widget (TextBox, (Rich)TextArea, CheckBox, Date
     * pop-up).
     *
     * @param field      (Mandatory)
     * @param style      (Optional), will be added to the styles
     * @param enabled    (Mandatory) when false, it will be a read-only widget
     * @param inputStyle (mandatory) When sets the appropriate CSS style (redoraFormInput or redoraTableInput)
     * @return TextArea, TextBox, EnumWidget and RichTextArea
     */
    public static Widget getInputWidget(Field field, String style, boolean enabled, InputStyle inputStyle) {
        String redoraInput = resources.css().redoraFormInput();
        if (inputStyle == table) {
            redoraInput = resources.css().redoraTableInput();
        }
        switch (field.type) {
            case String: {
                if (field.maxLength > 255) {
                    final TextArea textArea = new TextArea();
                    textArea.setName(field.name);
                    textArea.getElement().setId(field.inputID);
                    textArea.addStyleName(redoraInput);
                    if (field.helpInfo != null) {
                        textArea.setTitle(field.helpInfo);
                    }
                    if (field.notNull) {
                        textArea.addStyleName(resources.css().notNull());
                    }
                    if (style != null) {
                        textArea.addStyleName(style);
                    }
                    textArea.addStyleName(resources.css().redoraText());
                    textArea.setEnabled(enabled);

                    return textArea;
                } else {
                    final TextBox textBox = new TextBox();
                    textBox.setName(field.name);
                    textBox.getElement().setId(field.inputID);
                    textBox.addStyleName(redoraInput);
                    if (field.maxLength < 10) {
                        textBox.addStyleName(resources.css().redoraStringSmall());
                    } else if (field.maxLength < 50) {
                        textBox.addStyleName(resources.css().redoraStringMedium());
                    } else {
                        textBox.addStyleName(resources.css().redoraStringLarge());
                    }
                    if (field.helpInfo != null) {
                        textBox.setTitle(field.helpInfo);
                    }
                    textBox.setMaxLength(field.maxLength);
                    if (field.notNull) {
                        textBox.addStyleName(resources.css().notNull());
                    }
                    if (style != null) {
                        textBox.addStyleName(style);
                    }
                    textBox.setEnabled(enabled);

                    return textBox;
                }
            }
            case Integer: {
                final TextBox textBox = new TextBox();
                textBox.setName(field.name);
                textBox.getElement().setId(field.inputID);
                textBox.addStyleName(redoraInput);
                if (field.helpInfo != null) {
                    textBox.setTitle(field.helpInfo);
                }
                if (field.notNull) {
                    textBox.addStyleName(resources.css().notNull());
                }
                if (style != null) {
                    textBox.addStyleName(style);
                }
                textBox.addStyleName(resources.css().redoraInteger());
                textBox.setEnabled(enabled);

                return textBox;
            }
            case Long: {
                final TextBox textBox = new TextBox();
                textBox.setName(field.name);
                textBox.getElement().setId(field.inputID);
                textBox.addStyleName(redoraInput);
                if (field.helpInfo != null) {
                    textBox.setTitle(field.helpInfo);
                }
                if (field.notNull) {
                    textBox.addStyleName(resources.css().notNull());
                }
                if (style != null) {
                    textBox.addStyleName(style);
                }
                textBox.addStyleName(resources.css().redoraLong());
                textBox.setEnabled(enabled);

                return textBox;
            }
            case Double: {
                final TextBox textBox = new TextBox();
                textBox.setName(field.name);
                textBox.getElement().setId(field.inputID);
                textBox.addStyleName(redoraInput);
                if (field.helpInfo != null) {
                    textBox.setTitle(field.helpInfo);
                }
                if (field.notNull) {
                    textBox.addStyleName(resources.css().notNull());
                }
                if (style != null) {
                    textBox.addStyleName(style);
                }
                textBox.addStyleName(resources.css().redoraDouble());
                textBox.setEnabled(enabled);

                return textBox;
            }
            case Boolean: {
                final CheckBox checkBox = new CheckBox(field.name);
                checkBox.getElement().setId(field.inputID);
                checkBox.addStyleName(redoraInput);
                if (field.helpInfo != null) {
                    checkBox.setTitle(field.helpInfo);
                }

                checkBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
                    public void onValueChange(ValueChangeEvent<Boolean> event) {
                        if (checkBox.getValue()) {
                            checkBox.setFormValue("true");
                        } else {
                            checkBox.setFormValue("false");
                        }
                    }
                });
                if (field.notNull) {
                    checkBox.addStyleName(resources.css().notNull());
                }
                if (style != null) {
                    checkBox.addStyleName(style);
                }
                //checkBox.addStyleName(resources.css().redoraBoolean());
                checkBox.setEnabled(enabled);

                return checkBox;
            }
            case Date: {
                final DateBox date = new DateBox(field.name, field.inputID, DATE);
                date.addStyleName(redoraInput);

                if (field.helpInfo != null) {
                    date.setTitle(field.helpInfo);
                }
                if (field.notNull) {
                    date.addStyleName(resources.css().notNull());
                }
                if (style != null) {
                    date.addStyleName(style);
                }
                date.setEnabled(enabled);
                return date;
            }
            case Datetime: {
                final DateBox date = new DateBox(field.name, field.inputID, DATETIME);
                date.addStyleName(redoraInput);

                if (field.helpInfo != null) {
                    date.setTitle(field.helpInfo);
                }
                if (field.notNull) {
                    date.addStyleName(resources.css().notNull());
                }
                if (style != null) {
                    date.addStyleName(style);
                }
                date.setEnabled(enabled);
                return date;
            }
            case Enum: {
                EnumWidget widget = new EnumWidget(field.name, 3, 3);
                widget.setId(field.inputID);
                widget.addStyleName(redoraInput);
                if (!field.notNull) {
                    widget.addItem("", "");
                } else {
                    widget.addStyleName(resources.css().notNull());
                }
                if (field.helpInfo != null) {
                    widget.setTitle(field.helpInfo);
                }
                if (style != null) {
                    widget.addStyleName(style);
                }
                //widget.addStyleName(resources.css().redoraEnum());
                //widget.setEnabled(enabled);

                return widget;
            }
            case Html: {
                final RichTextArea richText = new RichTextArea(redoraInput, field.inputID);
                if (field.helpInfo != null) {
                    richText.setTitle(field.helpInfo);
                }
                if (field.notNull) {
                    richText.addStyleName(resources.css().notNull());
                }
                if (style != null) {
                    richText.addStyleName(style);
                }
                //richText.setEnabled(enabled);

                return richText;
            }
            case Object: {
                ListBox listBox = new ListBox();
                listBox.setName(field.name);
                listBox.getElement().setId(field.inputID);
                listBox.addStyleName(redoraInput);
                if (!field.notNull) {
                    listBox.addItem("", "");
                } else {
                    listBox.addStyleName(resources.css().notNull());
                }
                if (field.helpInfo != null) {
                    listBox.setTitle(field.helpInfo);
                }
                if (style != null) {
                    listBox.addStyleName(style);
                }
                //listBox.addStyleName(redoraObject.name());
                listBox.setEnabled(enabled);

                return listBox;
            }
            default: {
                l.log(SEVERE, "Widget not defined for " + field.type);
                return null;
            }
        }
    }

    public static Label getFormLabel(Field field) {
        final Label label = new Label(field.displayNameForm, field.inputID);
        if (field.helpInfo != null) {
            label.setTitle(field.helpInfo);
        }
        label.addStyleName(resources.css().redoraFormLabel());
        if (field.notNull) {
            label.addStyleName(resources.css().notNull());
        }
        return label;
    }

    public static Widget getWidgetById(Field field, HasWidgets panel) {
        Iterator<Widget> iterator = panel.iterator();
        while (iterator.hasNext()) {
            Widget ret = iterator.next();
            if (field.inputID.equals(ret.getElement().getId())) {
                return ret;
            } else if (ret instanceof HasWidgets) {
                Widget temp = getWidgetById(field, (HasWidgets) ret);
                if (temp != null)
                    return temp;
            }
        }
        l.log(INFO, "Didn't find Widget " + field.name);
        return null;
    }

    public static Label getViolatedMessagelabel(Field field) {
        final Label label = new Label();
        label.setStyleName(resources.css().redoraBRViolatedLabel());
        label.getElement().setId(field.inputID + ".violatedMessage");
        label.setVisible(false);
        return label;
    }

    public static Label getViolatedMessagelabelById(Field field, HasWidgets panel) {
        Iterator<Widget> iterator = panel.iterator();
        while (iterator.hasNext()) {
            Widget ret = iterator.next();
            String labelId = field.inputID + ".violatedMessage";
            if (labelId.equals(ret.getElement().getId())) {
                return (Label) ret;
            } else if (ret instanceof HasWidgets) {
                Label temp = getViolatedMessagelabelById(field, (HasWidgets) ret);
                if (temp != null)
                    return temp;
            }
        }
        l.log(INFO, "Didn't find message label for " + field.name);
        return null;
    }


    public static boolean NotNull(Object obj) {
        return obj != null && !NULL.equals(obj.toString())
                && obj.toString().length() != 0;
    }

    public static Button submit(final FormPanel form) {
        final Button submit = new Button(constants.save(), new ClickHandler() {
            public void onClick(ClickEvent event) {
                form.submit();
            }
        });
        return submit;
    }

    public static Button close(final PopupPanel popup) {
        final Button close = new Button(constants.close(), new ClickHandler() {
            public void onClick(ClickEvent event) {
                //RootPanel.get("messageLabel").clear();
                popup.hide();
            }
        });
        return close;
    }

    public static FormPanel form(String actionUrl) {
        FormPanel form = new FormPanel();
        form.setAction(actionUrl);
        form.setEncoding(ENCODING_URLENCODED);
        form.setMethod(METHOD_POST);
        return form;
    }

    public static Field from(String fieldName, Fields fields) {
        for (Field field : fields.fields()) {
            if (field.name.equals(fieldName)) {
                return field;
            }
        }
        return null;
    }

    public static boolean isShow = false;

    public static void displayError(String error) {
        isShow = true;
        final Element message = messageLabel();
        message.setClassName(resources.css().redoraErrorMessage());
        message.setInnerHTML(error);
        message.getStyle().setDisplay(Style.Display.INLINE);
        isShow = false;

        Timer timer = new Timer() {

            @Override
            public void run() {
                message.getStyle().setDisplay(Style.Display.NONE);
            }
        };
        timer.schedule(10000);
    }

    public static void displayInfo(String info) {
        isShow = true;
        final Element message = messageLabel();
        message.setClassName(resources.css().redoraInfoMessage());
        message.setInnerHTML(info);
        message.getStyle().setDisplay(Style.Display.INLINE);
        isShow = false;

        Timer timer = new Timer() {

            @Override
            public void run() {
                message.getStyle().setDisplay(Style.Display.NONE);
            }
        };
        timer.schedule(10000);
    }

    public static com.google.gwt.user.client.ui.Label createMessageLabel() {
        final com.google.gwt.user.client.ui.Label retVal = new com.google.gwt.user.client.ui.Label();
        retVal.getElement().setId("RedoraMessageLabel");
        retVal.setVisible(false);

        retVal.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                retVal.setVisible(false);
            }
        });
        l.log(INFO, "Message label is created");
        return retVal;
    }

    static Element messageLabel() {
        NodeList<Element> labels = RootLayoutPanel.get().getElement().getElementsByTagName("Div");
        for (int i = 0; i < labels.getLength(); i++) {
            if ("RedoraMessageLabel".equals(labels.getItem(i).getId())) {
                return labels.getItem(i);
            }
        }
        return null;
    }

    static DateTimeFormat MONTH_ABBR_DAY =
            DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.MONTH_ABBR_DAY);

    static DateTimeFormat HH_MM_FORMAT =
            DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.HOUR24_MINUTE);

    static DateTimeFormat YEAR_MONTH_DAY =
            DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.YEAR_MONTH_DAY);

    public static String formatShortTime(Date date) {
        if (date == null)
            return null;
        String time;
        Date now = new Date();

        if (now.getYear() == date.getYear()
                && now.getDay() == date.getDay())
            time = HH_MM_FORMAT.format(date);
        else if (now.getYear() == date.getYear() && now.getDay() != date.getDay())
            time = MONTH_ABBR_DAY.format(date);
        else {
            time = YEAR_MONTH_DAY.format(date);
        }
        return time;
    }

    public static String formatFullTime(Date date) {
        if (date != null)
            return YEAR_MONTH_DAY.format(date) + " " + HH_MM_FORMAT.format(date);
        return null;
    }
}
